package es.ull.opendata.px2scovo.scovo;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.StringUtils;

import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.Resource;

import es.ull.opendata.px2scovo.pcaxis.px.Category;
import es.ull.opendata.px2scovo.pcaxis.px.PX;
import es.ull.opendata.px2scovo.pcaxis.px.PXException;
import es.ull.opendata.px2scovo.pcaxis.px.Variable;

public class ScovoDataset {
	
	// SCOVO namespaces
	private final String dimensionSuperclassNamespace = "http://purl.org/NET/scovo#Dimension";
	private final String datasetSuperclassNamespace = "http://purl.org/NET/scovo#Dataset";
	private final String itemSuperclassNamespace = "http://purl.org/NET/scovo#Item";
	private final String datasetOfNamespace = "http://purl.org/NET/scovo#datasetOf";
	private final String datasetNamespace = "http://purl.org/NET/scovo#dataset";
	private final String dimensionNamespace = "http://purl.org/NET/scovo#dimension";
	
	// RDF-SCHEMA namespaces
	private final String subclassOfNamespace = "http://www.w3.org/2000/01/rdf-schema#subClassOf";
	private final String typeNamespace = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type";	
	private final String valueNamespace = "http://www.w3.org/1999/02/22-rdf-syntax-ns#value";	
	
	
	private Model model;
	private PX px;
	private NamespacesManager namespaces;
	
	public ScovoDataset() {
		model = ModelFactory.createDefaultModel();
		px = new PX();
		namespaces = new NamespacesManager();
	}
	
	public boolean loadPX (String pxFileName) throws PXException, ScovoDatasetException, NamespacesManagerException, IOException {
		px.loadPX(pxFileName);
		if (!px.isValidPX()) {
			throw new ScovoDatasetException("PX file does not valid");
		}
		putDimensions();
		ArrayList<Integer> categoriesIndex = getCategoriesIndex(px.getVariables());
		putDataset(categoriesIndex);
		putItems(categoriesIndex);
		return true;
	}
	
	private void putDimensions () throws NamespacesManagerException {
		for (Variable variable : px.getVariables()) {
			String variableResourseString = namespaces.getNamespace(variable.getName());
			Resource variableResource = model.createResource(variableResourseString);
			Property variableProperty = model.createProperty(subclassOfNamespace);
			Resource variableLiteral = model.createResource(dimensionSuperclassNamespace);
			model.add(variableResource, variableProperty, variableLiteral);
			List<Category> categories = variable.getCategories();
			for (int j = 0; j < categories.size(); j++) {
				Resource categoryResource = model.createResource(namespaces.getNamespace(categories.get(j).getName()));
				Property categoryProperty = model.createProperty(typeNamespace);
				Resource categoryLiteral = model.createResource(variableResourseString);
				model.add(categoryResource, categoryProperty, categoryLiteral);
			}
		}
	}
	
	private ArrayList<String> dimensionsOfData (List<Variable> variables, ArrayList<Integer> categoriesIndex, int index) {
		ArrayList<String> listOfDimensions = new ArrayList<String>();
		int numberOfData = index;
		for (int i = 0; i < variables.size() - 1; i++) {
			int it = 0;
			while (numberOfData >= categoriesIndex.get(i)) {
				numberOfData -= categoriesIndex.get(i);
				it++;
			}
			listOfDimensions.add(variables.get(i).getCategories().get(it).getName());
		}
		int it = 0;
		while (numberOfData >= categoriesIndex.get(categoriesIndex.size() - 1)) {
			numberOfData -= categoriesIndex.get(categoriesIndex.size() - 1); 
			it++;
		}
		listOfDimensions.add(variables.get(variables.size() - 1).getCategories().get(it).getName());
		return listOfDimensions;
	}
	
	/**
	 * 
	 * The name of the Data will be the Codes (if the Code is blank, the name) of its dimensions separate with -
	 * 
	 * @param variables
	 * @param categoriesIndex 
	 * @param index index of the data
	 * @return The name of the data 
	 */
	private String nameData (List<Variable> variables, ArrayList<Integer> categoriesIndex, int index) {
		String nameData = px.getSurvey() + "-";
		int numberOfData = index;
		for (int i = 0; i < variables.size() - 1; i++) {
			int it = 0;
			while (numberOfData >= categoriesIndex.get(i)) {
				numberOfData -= categoriesIndex.get(i);
				it++;
			}
			if (StringUtils.isBlank(variables.get(i).getCategories().get(it).getCode())) {
				nameData += variables.get(i).getCategories().get(it).getName() + "-";				
			}
			else {
				nameData += variables.get(i).getCategories().get(it).getCode() + "-";
			}
		}
		int it = 0;
		while (numberOfData >= categoriesIndex.get(categoriesIndex.size() - 1)) {
			numberOfData -= categoriesIndex.get(categoriesIndex.size() - 1); 
			it++;
		}
		if (StringUtils.isBlank(variables.get(variables.size() - 1).getCategories().get(it).getCode())) {
			nameData += variables.get(variables.size() - 1).getCategories().get(it).getName();				
		}
		else {
			nameData += variables.get(variables.size() - 1).getCategories().get(it).getCode();
		}
		return nameData;
	}
	
	
	private ArrayList<Integer> getCategoriesIndex (List<Variable> variables) {
		ArrayList<Integer> categoriesIndex = new ArrayList<Integer>();
		for (int i = 0; i < variables.size(); i++) {
			Integer categoryIndex = 1;
			for (int j = i + 1; j < variables.size(); j++) {
				categoryIndex *= variables.get(j).getCategories().size();
			}
			categoriesIndex.add(categoryIndex);
		}
		return categoriesIndex;
	}
	
	
	private void putDataset (ArrayList<Integer> categoriesIndex) throws NamespacesManagerException {
		Resource surveyResource = model.createResource(namespaces.getNamespace(px.getSurvey()));
		Property survetProperty = model.createProperty(typeNamespace);
		Resource surveyLiteral = model.createResource(datasetSuperclassNamespace);
		model.add(surveyResource, survetProperty, surveyLiteral);
		List<String> datas = px.getData();
		for (int i = 0; i < datas.size(); i++) {
			Property dataProperty = model.createProperty(datasetOfNamespace);
			Resource dataLiteral = model.createResource(namespaces.getNamespace(nameData(px.getVariables(), categoriesIndex, i)));
			model.add(surveyResource, dataProperty, dataLiteral);
		}
	}
	
	private void putItems (ArrayList<Integer> categoriesIndex) throws NamespacesManagerException {
		List<String> data = px.getData();
		for (int i = 0; i < data.size(); i++) {
			// type item
			Resource itemResource = model.createResource(namespaces.getNamespace(nameData(px.getVariables(), categoriesIndex, i)));
			Property itemTypeProperty = model.createProperty(typeNamespace);
			Resource itemLiteral = model.createResource(itemSuperclassNamespace);
			model.add(itemResource, itemTypeProperty, itemLiteral);
			// value
			Property valueProperty = model.createProperty(valueNamespace);
			Literal valueLiteral = model.createLiteral(data.get(i));
			model.add(itemResource, valueProperty, valueLiteral);
			// Dataset
			Property datasetProperty = model.createProperty(datasetNamespace);
			Resource datasetLiteral = model.createResource(namespaces.getNamespace(px.getSurvey()));
			model.add(itemResource, datasetProperty, datasetLiteral);
			// Dimensions
			ArrayList<String> dimensionsOfData = dimensionsOfData(px.getVariables(), categoriesIndex, i);
			for (int j = 0; j < dimensionsOfData.size(); j++) {
				Property dimensionProperty = model.createProperty(dimensionNamespace);
				Resource dimensionLiteral = model.createResource(namespaces.getNamespace(dimensionsOfData.get(j)));
				model.add(itemResource, dimensionProperty, dimensionLiteral);
			}
		}
	}
	
	public void save2File(String filename) throws FileNotFoundException {
		File file = new File(filename);
		FileOutputStream fos = new FileOutputStream(file);
		model.write(fos);
	}
	
	
	public NamespacesManager getNamespaces() {
		return namespaces;
	}
	
	public Model getModel() {
		return model;
	}
	
	public static void main(String[] args) throws NamespacesManagerException {
		ScovoDataset s = new ScovoDataset();
		try {
			s.loadPX("./resources/PX/valuesNumberDiferentCodesNumber.px");
			s.save2File("/home/sergio/superSCOVO.rdf");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (PXException e) {
			e.printStackTrace();
		} catch (ScovoDatasetException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
